/*!
 * OSAssistant is the assistant library easy to develop opensocial application.
 * OSAssistant is copyrighted free software by Junheon Lee <jh.cloudnine@gmail.com>.
 */

if (!window.OSAssistant) {
    /**
     * @namespace Contains all features.
     */
    var OSAssistant = {};
}

// get opensocial container
OSAssistant.os = opensocial.Container.get();

OSAssistant.$A = function (arraylike) {
    var len = arraylike.length, a = [];
    while (len--) {
        a[len] = arraylike[len];
    }
    return a;
};

OSAssistant.$ = function (id) {
    return document.getElementById(id);
};

OSAssistant.extend = function (destination, source) {
    for (var property in source) if (source.hasOwnProperty(property)) {
        destination[property] = source[property];
    }

    return destination;
};

OSAssistant.Class = function () {
    var parent = null, properties = OSAssistant.$A(arguments), key;

    if (typeof properties[0] === "function") {
        parent = properties.shift();
    }

    function klass() {
        this.initialize.apply(this, arguments);
    }

    if (parent) {
        for (key in parent.prototype) if (parent.prototype.hasOwnProperty(key)) {
            klass.prototype[key] = parent.prototype[key];
        }
    }

    for (key in properties[0]) if(properties[0].hasOwnProperty(key)) {
        klass.prototype[key] = properties[0][key];
    }

    if (!klass.prototype.initialize) {
        klass.prototype.initialize = function() {};
    }

    klass.prototype.constructor = klass;

    return klass;
};

/**
 * 이벤트 핸들러를 등록한다.
 *
 * @param {Element} element 대상 엘레먼트
 * @param {String} eventName 이벤트명. 예) keydown
 * @param {Function} handler 이벤트 핸들러
 */
OSAssistant.observe = function (element, eventName, handler) {
    if (element.addEventListener) {
        element.addEventListener(eventName, handler, false);
    }
    else {
        element.attachEvent('on' + eventName, handler);
    }

    element = null;
};

OSAssistant.stopObserving = function (element, eventName, handler) {
    if (element.removeEventListener) {
        element.removeEventListener(eventName, handler, false);
    }
    else {
        element.detachEvent('on' + eventName, handler);
    }

    element = null;
};

/**
 * @namespace Contains all about the utility method
 */
OSAssistant.Util = {};

/**
 * Debug Popup
 *
 */
OSAssistant.Util.Debug = function (error) {
    var message = (error instanceof Error) ?
                        ('[' + error.fileName + ']' + error.lineNumber + ' : (' + error.name + ') ' + error.message) :
                        error;

    if (typeof console !== 'undefined' && typeof console.log !== 'undefined') {
        console.log(message);
    }

    var body = document.getElementsByTagName('body')[0];

    body.appendChild(
        $D('div',
            {
                id: 'debugPopup',
                className: 'debugPopup'
            }, 
            [
                $D('p', message),
                $D('button', {
                    click: function () {
                        var element = document.getElementById('debugPopup');
                        element.parentNode.removeChild(element);
                    }
                }, 'Close')
            ]
        )
    );
};

/**
 * Debug Popup - alias of OSAssistant.Util.Debug
 *
 * @param {Error} error error message or error instance 
 */
var Debug = function (error) {
    OSAssistant.Util.Debug(error);
};


OSAssistant.Util.Request = OSAssistant.Class(/** @lends OSAssistant.Util.Request.prototype */ {
    /**
     * opensocial v0.8의 gadgets.io.makeRequest를 사용하여 외부 서버에 요청을 보낸다.
     *
     * @constructs
     *
     * @param {Object} params 외부 서버로 요청에 사용될 parameters
     * @param {String} params.url 외부 서버 URL 
     * @param {Object} params.parameters 외부 서버로 전달할 Post Data 
     * @param {Function} params.onLoading 요청이 시작될때 실행될 function
     * @param {Function} params.onComplete 요청이 완료되었을 때 실행될 function 
     *
     * @example
     * new OSAssistant.Util.Request({
     *      url: 'http://test.com/member/update',
     *      parameters: {
     *          name: 'bob',
     *          age: 25
     *      },
     *      onLoading: function () {
     *          ...
     *      },
     *      onComplete: function (res) {
     *          var data = res.data; // JSON data
     *          ...
     *      }
     * });
     *
     * //축약형
     *
     * $request({
     *      url: 'http://test.com/member/update',
     *      parameters: {
     *          name: 'bob',
     *          age: 25
     *      },
     *      onLoading: function () {
     *          ...
     *      },
     *      onComplete: function (res) {
     *          var data = res.data; // JSON data
     *          ...
     *      }
     * });
     *
     */
    initialize: function (params) {
        this.setParams(params);
        this.request();
    },
    /**
     * 요청에 사용될 parameter를 설정한다.
     *
     * @param {Object} params 외부 서버로 요청에 사용될 parameters
     * @param {String} params.url 외부 서버 URL 
     * @param {Object} params.parameters 외부 서버로 전달할 Post Data 
     * @param {Function} params.onLoading 요청이 시작될때 실행될 function
     * @param {Function} params.onComplete 요청이 완료되었을때 실행될 function 
     */
    setParams: function (params) {
        if (typeof params.url === 'string') {
            /**
             * 외부 서버 URL 
             * @type string 
             */
            this.url = params.url;
        }

        /**
         * Post Data와 요청 방식 
         * @type object
         */
        this.params = {};
        this.params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.JSON;
        this.params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.POST;
        this.params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.SIGNED;
        if (typeof params.parameters === 'object') {
            this.params[gadgets.io.RequestParameters.POST_DATA] = gadgets.io.encodeValues(params.parameters);
        }

        if (typeof params.onLoading === 'function') {
            /**
             * 요청이 시작될때 실행될 function
             * @type function
             */
            this.onLoading = params.onLoading;
        }
        
        if (typeof params.onComplete === 'function') {
            /**
             * 요청이 완료되었을때 실행될 function 
             * @type function
             */
            this.onComplete = params.onComplete;
        }
    },
    /*
     * 외부 서버에 요청을 보낸다.
     */
    request: function () {
        if (typeof this.onLoading === 'function') {
            this.onLoading();
        }
        gadgets.io.makeRequest(this.url, this.onComplete, this.params);
    }
});

/**
 * opensocial v0.8의 gadgets.io.makeRequest를 사용하여 외부 서버에 요청을 보낸다.
 *
 * @param {Object} params 외부 서버로 요청에 사용될 parameters
 * @param {String} params.url 외부 서버 URL 
 * @param {Object} params.parameters 외부 서버로 전달할 Post Data 
 * @param {Function} params.onLoading 요청이 시작될때 실행될 function
 * @param {Function} params.onComplete 요청이 완료되었을 때 실행될 function 
 *
 * @returns {OSAssistant.Util.Request}
 *
 * @example
 * new OSAssistant.Util.Request({
 *      url: 'http://test.com/member/update',
 *      parameters: {
 *          name: 'bob',
 *          age: 25
 *      },
 *      onLoading: function () {
 *          ...
 *      },
 *      onComplete: function (res) {
 *          var data = res.data; // JSON data
 *          ...
 *      }
 * });
 *
 * //축약형
 *
 * $request({
 *      url: 'http://test.com/member/update',
 *      parameters: {
 *          name: 'bob',
 *          age: 25
 *      },
 *      onLoading: function () {
 *          ...
 *      },
 *      onComplete: function (res) {
 *          var data = res.data; // JSON data
 *          ...
 *      }
 * });
 *
 */
var $request = function (url, options) {
    return new OSAssistant.Util.Request(url, options);
};


/**
 * @namespace Contains all about the data request to the Container.
 */
OSAssistant.Data = {};

OSAssistant.Data.Person = OSAssistant.Class(/** @lends OSAssistant.Data.Person.prototype */ {
    /**
     * opensocial의 한명의 user에 대한 정보 
     *
     * @constructs
     *
     * @param {Object} params user의 정보
     * @param {String} params.uid user의 uid
     * @param {String} params.name user의 이름
     * @param {String} params.thumb user의 섬네일 이미지
     * @param {String} params.profileLink user의 profile link
     * @param {Boolean} params.hasAppAdded user가 app을 가입했는지 안했는지 구분
     */
    initialize: function (params) {
        if (typeof params.uid === 'string') {
            /**
             * user의 uid
             * @type string
             */
            this.uid = params.uid;
        }

        if (typeof params.name === 'string') {
            /**
             * user의 이름
             * @type string
             */
            this.name = params.name;
        }

        if (typeof params.thumb === 'string') {
            /**
             * user의 섬네일 이미지
             * @type string
             */
            this.thumb = params.thumb;
        }

        if (typeof params.profileLink === 'string') {
            /**
             * user의 profile link
             * @type string
             */
            this.profileLink = params.profileLink;
        }

        if (typeof params.address === 'string') {
            this.address = params.address;
        }

        if (typeof params.hasAppAdded === 'boolean') {
            /**
             * user가 app을 가입했는지 안했는지 구분
             * @type boolean
             */
            this.hasAppAdded = params.hasAppAdded;
        }
    }
});

OSAssistant.Data.People = OSAssistant.Class(/** @lends OSAssistant.Data.People.prototype */ {
    /**
     * opensocial container로부터 받아온 친구 정보를 저장한다.
     *
     * @constructs
     */
    initialize: function () {
        /**
         * 실제 친구 정보가 저장되는 object
         * @type object
         */
        var data = {};

        /**
         * 친구 정보를 추가한다.
         *
         * @param {String} uid 친구의 uid
         * @param {Object} value 친구의 정보 
         *
         * @example
         *
         * var friends = new OSAssistant.Data.People();
         * friends.set('uid', {
         *      thumb: '....',
         *      profileLink: '....'
         *      .
         *      .
         *      .
         * });
         */
        this.set = function (uid, value) {
            if (value instanceof OSAssistant.Data.Person) {
                data[uid] = value;
            }
        };

        /**
         * 모든 친구의 uid를 Array로 받아온다.
         *
         * @returns {Array}
         */
        this.uids = function () {
            var results = [];

            for (var uid in data) if (data.hasOwnProperty(uid)) {
                results.push(uid);
            }

            return results;
        };

        /**
         * uid로 친구의 정보를 받아온다.
         * @param {String} uid 친구의 uid
         *
         * @returns {OSAssistant.Data.Person}
         */ 
        this.get = function (uid) {
            return data[uid];
        };

        /**
         * 모든 친구에 대해 반복하여 iteration을 수행한다.
         * 
         * @param {Function} iterator 모든 친구에 대해 수행할 function
         */
        this.each = function (iterator) {
            for (var uid in data) if (data.hasOwnProperty(uid)) {
                iterator(data[uid]);
            }
        };

        /**
         * 모든 친구의 정보를 Array로 변환하여 반환한다.
         *
         * @returns {Array}
         */
        this.toArray = function () {
            var results = [];

            for (var uid in data) if (data.hasOwnProperty(uid)) {
                results.push(data);
            }

            return results;
        };
    }
});

OSAssistant.Data.Owner = OSAssistant.Class(/** @lends OSAssistant.Data.Owner.prototype */ {
    /**
     * opensocial container로부터 owner의 정보를 받아온다.
     *
     * @constructs
     * @param {Function} callback owner의 정보를 받아온 후 실행할 function
     *
     * @example
     *
     * var owner = OSAssistant.Data.Owner(function (data, obj) {
     *      .
     *      .
     *      .
     * });
     *
     * var owner = $owner(function (data, obj) {
     *      .
     *      .
     *      .
     * });
     */
    initialize: function (callback) {
        var data = {}, that = this;

        /*
         * opensocial container로부터 받은 response에서 owner의 정보를 추출한다.
         *
         * @param {Object} response opensocial container로부터 받은 response 
         */
        var requestCallback = function (response) {
            var resData = response.get('OwnerInfo');
            
            if (response.hadError()) {
                Debug(resData.getErrorCode() + '\n' + resData.getErrorMessage());

                return false;
            }

            var owner = resData.getData();

            /**
             * owner의 opensocial container에 해당하는 uid
             * @type string
             */
            data.uid = owner.getId();

            /**
             * owner의 opensocial container에 해당하는 이름
             * @type string
             */
            data.name = owner.getDisplayName();

            /**
             * owner의 opensocial container에 해당하는 섬네일 이미지
             * @type string
             */
            data.thumb = owner.getField(opensocial.Person.Field.THUMBNAIL_URL);

            /**
             * owner의 opensocial container에 해당하는 profile link
             * @type string
             */
            data.profile = owner.getField(opensocial.Person.Field.PROFILE_URL);

            if (typeof callback === 'function') {
                callback(that);
            }
        };

        /**
         * opensocial container에 owner의 정보 요청을 보낸다.
         */
        this.request = function () {
            var dataReqObj = OSAssistant.os.newDataRequest();
            var ownerReq = dataReqObj.newFetchPersonRequest(opensocial.IdSpec.PersonId.OWNER);
            
            dataReqObj.add(ownerReq, 'OwnerInfo');

            dataReqObj.send(function (response) {
                requestCallback(response);
            });
        };

        this.getData = function (field) {
            return data[field] ? data[field] : false;
        };

        this.request();
    }
});

/**
 * opensocial container로부터 owner의 정보를 받아온다.
 *
 * @param {Function} callback owner의 정보를 받아온 후 실행할 function
 * @returns {OSAssistant.Data.Owner}
 *
 * @example
 *
 * var owner = OSAssistant.Data.Owner(function (data, obj) {
 *      .
 *      .
 *      .
 * });
 *
 * var owner = $owner(function (data, obj) {
 *      .
 *      .
 *      .
 * });
 */
var $owner = function (callback) {
    return new OSAssistant.Data.Owner(callback);
};

OSAssistant.Data.Viewer = OSAssistant.Class(/** @lends OSAssistant.Data.Viewer.prototype */ {
    /**
     * opensocial container로부터 viewer의 정보를 받아온다.
     *
     * @constructs
     * @param {Function} callback viewer의 정보를 받아온 후 실행할 function
     *
     * @example
     *
     * var viewer = OSAssistant.Data.Viewer(function (data, obj) {
     *      .
     *      .
     *      .
     * });
     *
     * var viewer = $viewer(function (data, obj) {
     *      .
     *      .
     *      .
     * });
     */
    initialize: function (callback) {
        var data = {}, that = this;

        /*
         * opensocial container로부터 받은 response에서 viewer의 정보를 추출한다.
         *
         * @param {Object} response opensocial container로부터 받은 response 
         */
        var requestCallback = function (response) {
            var resData = response.get('ViewerInfo');
            
            if (response.hadError()) {
                Debug(resData.getErrorCode() + '\n' + resData.getErrorMessage());

                return false;
            }

            var viewer = resData.getData();

            /**
             * viewer의 opensocial container에 해당하는 uid
             * @type string
             */
            data.uid = viewer.getId();

            /**
             * viewer의 opensocial container에 해당하는 이름
             * @type string
             */
            data.name = viewer.getDisplayName();

            /**
             * viewer의 opensocial container에 해당하는 섬네일 이미지
             * @type string
             */
            data.thumb = viewer.getField(opensocial.Person.Field.THUMBNAIL_URL);

            /**
             * viewer의 opensocial container에 해당하는 profile link
             * @type string
             */
            data.profile = viewer.getField(opensocial.Person.Field.PROFILE_URL);

            if (typeof callback === 'function') {
                callback(that);
            }
        };

        /**
         * opensocial container에 viewer의 정보 요청을 보낸다.
         */
        this.request = function () {
            var dataReqObj = OSAssistant.os.newDataRequest();
            var viewerReq = dataReqObj.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER);
            
            dataReqObj.add(viewerReq, 'ViewerInfo');

            dataReqObj.send(function (response) {
                requestCallback(response);
            });
        };

        this.getData = function (field) {
            return data[field] ? data[field] : false;
        };

        this.request();
    }
});

/**
 * opensocial container로부터 viewer의 정보를 받아온다.
 *
 * @param {Function} callback viewer의 정보를 받아온 후 실행할 function
 * @returns {OSAssistant.Data.Viewer}
 *
 * @example
 *
 * var viewer = OSAssistant.Data.Viewer(function (data, obj) {
 *      .
 *      .
 *      .
 * });
 *
 * var viewer = $viewer(function (data, obj) {
 *      .
 *      .
 *      .
 * });
 */
var $viewer = function (callback) {
    return new OSAssistant.Data.Viewer(callback);
};

OSAssistant.Data.Friends = OSAssistant.Class(/** @lends OSAssistant.Data.Friends.prototype */ {
    /**
     * opensocial container로부터 친구의 정보를 받아온다.
     *
     * @constructs
     * @param {Object} options opensocial container로부터 친구의 정보를 받아오기 위한 설정 
     * @param {Integer} options.itemsPerPage 한번에 받아올 최대 친구 수 
     * @param {Function} options.onComplete 요청이 끝났을 때 실행할 function, 반환값: {OSAssistant.Data.People} 
     *
     * @example
     * new OSAssistant.Data.Friends({
     *      itemsPerPage: 20,
     *      onComplete: function (data) {
     *          data.each(function (item) {
     *              // item is instance of OSAssistant.Data.Person
     *              // item.uid, item.name, item.thumb, item.profileLink, item.hasAppAdded
     *          });
     *      }
     * });
     *
     * //축약형
     *
     * $friends({
     *      itemsPerPage: 20,
     *      onComplete: function (data) {
     *          data.each(function (item) {
     *              // item is instance of OSAssistant.Data.Person
     *              // item.uid, item.name, item.thumb, item.profileLink, item.hasAppAdded
     *          });
     *      }
     * });
     */
    initialize: function(options) {
        this.setParam(options);
        this.request();
    },
    /**
     * opensocial container로부터 친구의 정보를 받아오기 위한 설정을 한다.
     *
     * @param {Object} options opensocial container로부터 친구의 정보를 받아오기 위한 설정 
     * @param {Integer} options.itemsPerPage 한번에 받아올 최대 친구 수 
     * @param {Function} options.onComplete 요청이 끝났을 때 실행할 function, callback parameter: {OSAssistant.Data.People} 
     */
    setParam: function (options) {
        if (typeof options.itemsPerPage === 'number') {
            /**
             * 한번에 받아올 최대 친구 수
             * @type integer
             */
            this.itemsPerPage = options.itemsPerPage;
        }

        if (typeof options.onComplete === 'function') {
            /**
             * 요청이 끝날을 때 실행할 function, callback parameter: {OSAssistant.Data.People} 
             * @type function
             */
            this.onComplete = options.onComplete;
        }
        
        /**
         * 친구정보를 요청하기 위한 설정
         * @type object
         */
        this.params = {};

        this.params[opensocial.DataRequest.PeopleRequestFields.MAX] = this.itemsPerPage;
        this.params[opensocial.DataRequest.PeopleRequestFields.SORT_ORDER] = opensocial.DataRequest.SortOrder.NAME;
        this.params[opensocial.DataRequest.PeopleRequestFields.FILTER] = opensocial.DataRequest.FilterType.ALL;

        /**
         * 친구 정보 모음 
         * @type OSAssistant.Data.People 
         */
        this.data = new OSAssistant.Data.People();
    },
    /**
     * opensocial container에 친구 정보를 요청한다.
     */
    request: function () {
        var dataReqObj = OSAssistant.os.newDataRequest();

        var friendsIdSpec = opensocial.newIdSpec({'userId' : 'VIEWER', 'groupId' : 'FRIENDS'});
        var viewerFriendsReq = dataReqObj.newFetchPeopleRequest(friendsIdSpec, this.params);
        
        dataReqObj.add(viewerFriendsReq, 'FriendsInfo');

        var that = this;
        dataReqObj.send(function (response) {
            that.requestCallback(response);   
        });
    },
    /*
     * opensocial container로부터 받은 response에서 viewer의 정보를 추출한다.
     *
     * @param {Object} response opensocial container로부터 받은 response 
     */
    requestCallback: function (response) {
        var resData = response.get('FriendsInfo');

        if (response.hadError()) {
            Debug(resData.getErrorCode() + '\n' + resData.getErrorMessage());

            return false;
        }

        var obj = resData.getData();

        if (obj.size() > 0) {
            var that = this;
            obj.each(function (friend) {
                that.data.set(friend.getId(), new OSAssistant.Data.Person({
                    uid: friend.getId(),
                    name: friend.getDisplayName(),
                    thumb: friend.getField(opensocial.Person.Field.THUMBNAIL_URL),
                    profileLink: friend.getField(opensocial.Person.Field.PROFILE_URL),
                    address: friend.getField(opensocial.Person.Field.ADDRESSES),
                    hasAppAdded: friend.getField(opensocial.Person.Field.HAS_APP)
                }));
            });
        }
        
        if (obj.getTotalSize() >= this.params[opensocial.DataRequest.PeopleRequestFields.FIRST] + this.itemsPerPage) {
            this.moveToNext();
        }
        else if (typeof this.onComplete === 'function') {
            this.onComplete(this.data);
        }
    },
    /**
     * 다음 페이지의 친구 정보를 받아온다.
     */
    moveToNext: function () {
        this.params[opensocial.DataRequest.PeopleRequestFields.FIRST] += this.itemsPerPage;
        this.request();
    },
    /**
     * 친구 정보 모음을 받아온다.
     *
     * @returns {OSAssistant.Data.People}
     */
    getData: function () {
        return this.data;
    }
});

/**
 * opensocial container로부터 친구의 정보를 받아온다.
 *
 * @param {Object} options opensocial container로부터 친구의 정보를 받아오기 위한 설정 
 * @param {Integer} options.itemsPerPage 한번에 받아올 최대 친구 수 
 * @param {Function} options.onComplete 요청이 끝났을 때 실행할 function, 반환값: {OSAssistant.Data.People} 
 *
 * @returns {OSAssistant.Data.Friends}
 *
 * @example
 * $friends({
 *      itemsPerPage: 20,
 *      onComplete: function (data) {
 *          data.each(function (item) {
 *              // item is instance of OSAssistant.Data.Person
 *              // item.uid, item.name, item.thumb, item.profileLink, item.hasAppAdded
 *          });
 *      }
 * });
 */
var $friends = function (options) {
    return new OSAssistant.Data.Friends(options);
};

/**
 * @namespace Contains all about the UI class
 */
OSAssistant.UI = {};

OSAssistant.UI.Carousel = OSAssistant.Class({
    initialize: function (options) {
        var that = this;

        this.target = $(options.target);
        this.direction = options.direction;

        this.pageSize = options.size * options.itemsPerPage;
        this.current = 1;
        this.last = options.page;

        OSAssistant.observe(OSAssistant.$(options.prev), 'click', function () { that.prev(); return false; });
        OSAssistant.observe(OSAssistant.$(options.next), 'click', function () { that.next(); return false; });
    },
    prev: function () {
        if (this.current > 1) { this.current -= 1; }
        else { this.current = this.last; }
        this.move();
    },
    next: function () {
        if (this.current < this.last) { this.current += 1; }
        else { this.current = 1; }
        this.move();
    },
    move: function () {
        var length = '-' + (this.current - 1) * this.pageSize + 'px';

        if (this.direction === 'horizontal') {
            this.target.style.left = length;
        }
        else if (this.direction === 'vertical') {
            this.target.style.top = length;
        }
    }
});

OSAssistant.UI.Pagination = OSAssistant.Class({
    initialize: function (options) {
        var that, current, container, pageBtns, idx;

        container = OSAssistant.$(options.container);
        container.innerHTML = '';

        current = 1; that = this; pageBtns = [];

        for (idx = 1; idx <= options.pages; ++idx) {
            pageBtns[idx] = document.createElement('a')
            pageBtns[idx].href = '#?page=' + idx;
            pageBtns[idx].innerHTML = idx;

            (function () {
                var targetPage = idx;
                OSAssistant.observe(pageBtns[targetPage], 'click', function () {
                    that.move(targetPage);
                    return false;
                });
            }());

            pageBtns[idx].style.display = (idx <= 10) ? '' : 'none';
            container.appendChild(pageBtns[idx]);
        }

        pageBtns[current].className = options.toggle;

        this.prev = function () {
            if (current > 1) { that.move(current - 1); }
        };

        this.next = function () {
            if (current < options.pages) { that.move(current + 1); }
        };

        this.move = function (page) {
            if (page === current) {
                return false;
            }
            else if (page >= 1 && page <= options.pages) {
                var past = current;
                current = page;
                
                pageBtns[past].className = '';
                pageBtns[current].className = options.toggle;

                if (current % 10 === 1 && past < current) {
                    for (idx = current - 10; idx < current; ++idx) {
                        pageBtns[idx].style.display = 'none';
                    }

                    for (idx = current; idx - current < 10; ++idx) {
                        pageBtns[idx].style.display = '';
                        if (idx === options.pages) { break; }
                    }
                }

                if (current % 10 === 0 && past > current) {
                    for (idx = current - 9; idx <= current; ++idx) {
                        pageBtns[idx].style.display = '';
                    }

                    for (var idx = current + 1; idx <= current + 10; ++idx) {
                        pageBtns[idx].style.display = 'none';
                        if (idx === options.pages) { break; }
                    }
                }

                options.callback(current, past);
            }
        };

        OSAssistant.observe(OSAssistant.$(options.prev), 'click', function () {
            that.prev();
            return false;
        });

        OSAssistant.observe(OSAssistant.$(options.next), 'click', function () {
            that.next();
            return false;
        });
    }
});

OSAssistant.UI.Navigation = OSAssistant.Class(/** @lends OSAssistant.UI.Navigation */ {
    /**
     * Processes page navigation
     *
     * @constructs
     * @param {Object} options options
     */
    initialize: function (options) {
        try {
            this.setParam(options);
            this.setCurrentPage();
            this.handleNav();
        }
        catch (err) {
            Debug(err);
        }
    },
    setParam: function (options) {
        this.options = OSAssistant.extend({
            home: 'home',
            currentClass: 'current',
            postFix: 'Tab'
        }, options || {});
    },
    setCurrentPage: function () {
        var prefs = gadgets.views.getParams();
        this.options.current = prefs.page !== undefined ? prefs.page : this.options.home;
        $(this.options.current + this.options.postFix).addClassName(this.options.currentClass);
    },
    getCurrentPage: function () {
        return this.options.current;
    },
    handleNav: function () {
        var that = this;

        this.options.targetList.each(function (target) {
            if (that.getCurrentPage() !== target) {
                var el = document.getElementById(target + that.options.postFix);

                OSAssistant.observe(el, 'click', function () {
                    that.move(target);
                });
            }
        });
    },
    move: function (target) {
        var canvasView = new gadgets.views.View('canvas');
        gadgets.views.requestNavigateTo(canvasView, { page: target });
    }
});

OSAssistant.Feed = {};

OSAssistant.Feed.Invite = OSAssistant.Class({
    initialize: function (options) {
        var options = OSAssistant.extend({
            msg: null,
            friends: null,
            callback: function () {}
        }, options || {});

        var msg = opensocial.newMessage(options.msg);
        OSAssistant.os.requestShareApp(options.friends, msg, options.callback);
    }
});

OSAssistant.Feed.Activity = OSAssistant.Class({
    initialize: function (options) {
        var params = {};
        
        params[opensocial.Activity.Field.TITLE] = options.title;

        var activity = OSAssistant.os.newActivity(params);

        OSAssistant.os.requestCreateActivity(activity, opensocial.CreateActivityPriority.HIGH, function (status) {
            if (status.hadError()) {
                if (typeof options.callback === 'function') {
                    options.callback();
                }
            }
            else {
                if (typeof options.callback === 'function') {
                    options.callback();
                }
            }
        });
    }
});
